/****************************************************************************/
/*                                                                          */
/*   FILE:    sesscod.c		                                                */
/*   DATE:    02/07/97                                                      */
/*   PURPOSE: DVB-CI Session layer - SPDU Coding/Decoding Module			*/
/*			  conform to DVB-CI prEN 50221, june 1996						*/ 	
/*   TAB STOPS : 4															*/
/*   HISTORY:				                                 			    */
/*   Version    Date  		    Written by	     Notes	    	    		*/
/*	 1.0	    02/07/1997      SCM              Initial implementation 	*/
/*                                                                    	    */
/*--------------------------------------------------------------------------*/
/*   GENERAL COMMENTS                                                 	    */
/*		This module codes and decodes TPDU									*/
/*																		    */
/****************************************************************************/
#include <memory.h>
#include <stdio.h>
#include "itc.h"
#include "sm_trace.h"
#include "session.h"			


/* ======================================================================== */
/* NAME : SRPT_Code						 									*/
/* 									    									*/
/* DESCRIPTION : code a SPDU												*/
/* 									    									*/
/* INPUT PARAMETERS :							    						*/
/*		bSpduTag		: type of SPDU												*/
/*		ssnb	: session number											*/
/*		pXpdu	: parameters dependent of the tag							*/
/*									 										*/
/* OUTPUT PARAMETERS :							   							*/
/*		spdu	: pointer to coded SPDU	buffer								*/
/*									    									*/
/* RETURN CODE								    							*/
/*		 -1 : error while coding											*/
/*		> 0 : length of coded SPDU											*/
/*									  										*/
/* ======================================================================== */
int SESS_Code(unsigned char bSpduTag, unsigned short ssnb, xpdu_t *pXpdu, 
			  unsigned char *pbSpdu)
{
	int				spduLen = 0;		/* return code 				   */
	unsigned char*	ptr;	    /* pointer to allocated memory */

	SM_TRACE_ENTRY(TRMOD_SESS, "SESS_Code() - entry,bSpduTag=%s(0x%02X),ssnb=%d,pXpdu=0x%08X,pbSpdu=0x%08X\r\n",
					XMSG_NAME(bSpduTag), bSpduTag,ssnb,pXpdu,pbSpdu);

	ptr = pbSpdu;		    /* points the allocated buffer */

	/* Coding */			
	switch (bSpduTag)
	{
	case dutag_session_number :
		*ptr++ = bSpduTag;				/* bSpduTag 				*/
		*ptr++ = 2;		            	/* length_field 	*/
		SESS_CodSsnb(ssnb, ptr);     	/* ssnb	*/
		ptr += 2;
		memcpy(ptr, pXpdu->m_du.m_xpdu_session_number.m_pbData, pXpdu->m_du.m_xpdu_session_number.m_len);	/* APDU */		
		spduLen = 4 + pXpdu->m_du.m_xpdu_session_number.m_len;
		break;

	case dutag_open_session_response : 
		*ptr++ = bSpduTag;     				/* bSpduTag				*/
		*ptr++ = 3 + RESOURCE_ID_SIZE;     	 	/* length_field 	*/
		*ptr++ = pXpdu->m_du.m_xpdu_open_session_response.m_status; /* session_status	*/
		*ptr++ = (unsigned char)((pXpdu->m_du.m_xpdu_open_session_response.m_ressId>>24)&0xFF); /* resource_identifier */		
		*ptr++ = (unsigned char)((pXpdu->m_du.m_xpdu_open_session_response.m_ressId>>16)&0xFF);
		*ptr++ = (unsigned char)((pXpdu->m_du.m_xpdu_open_session_response.m_ressId>>8)&0xFF);
		*ptr++ = (unsigned char)(pXpdu->m_du.m_xpdu_open_session_response.m_ressId&0xFF);
		SESS_CodSsnb(ssnb, ptr);     	/* ssnb	*/
		spduLen = 5 + RESOURCE_ID_SIZE;
		break;
						
	case dutag_close_session_request : 
		*ptr++ = bSpduTag;     				/* bSpduTag				*/
		*ptr++ = 2;			     	 	/* length_field 	*/
		SESS_CodSsnb(ssnb, ptr);     	/* ssnb	*/
		spduLen = 4;
		break;
						
	case dutag_close_session_response : 
		*ptr++ = bSpduTag;     				/* bSpduTag				*/
		*ptr++ = 3;			     	 	/* length_field 	*/
		*ptr++ = pXpdu->m_du.m_xpdu_close_session_response.m_status; /* session_status	*/
		SESS_CodSsnb(ssnb, ptr);     	/* ssnb	*/
		spduLen = 5;
		break;
						
	case dutag_create_session_request : 
		*ptr++ = bSpduTag;     				/* bSpduTag				*/
		*ptr++ = 2 + RESOURCE_ID_SIZE;     	 	/* length_field 	*/
		*ptr++ = (unsigned char)((pXpdu->m_du.m_xpdu_create_session_request.m_ressId>>24)&0xFF); /* resource_identifier */		
		*ptr++ = (unsigned char)((pXpdu->m_du.m_xpdu_create_session_request.m_ressId>>16)&0xFF);
		*ptr++ = (unsigned char)((pXpdu->m_du.m_xpdu_create_session_request.m_ressId>>8)&0xFF);
		*ptr++ = (unsigned char)(pXpdu->m_du.m_xpdu_create_session_request.m_ressId&0xFF);
		SESS_CodSsnb(ssnb, ptr);     	/* ssnb	*/
		spduLen = 4 + RESOURCE_ID_SIZE;
		break;

	case dutag_open_session_request : 
			SM_TRACE_ERROR(TRMOD_SESS, "SESS_Code() - dutag_open_session_request is only for module\r\n");
			break;
	default :     
		SM_TRACE_ERROR(TRMOD_SESS, "SESS_Code() - Unknown Tag 0x%x\r\n", bSpduTag);
		spduLen = 0;
		break;
	}

	if (spduLen > 0)
	{
		SM_TRACE_BYTES(TRMOD_SESS, TRLEV_NOTIFY, pbSpdu, spduLen, "SESS_Code() - code spdu:");
	}

	SM_TRACE_RETURN(TRMOD_SESS, "SESS_Code() - return,spduLen=%d\r\n",spduLen);
	return spduLen;				/* return the length of SPDU */
}


/* ======================================================================== */
/* NAME : SESS_Decode						 								*/
/* 									    									*/
/* DESCRIPTION : decode a SPDU												*/
/* 									    									*/
/* INPUT PARAMETERS :							    						*/
/*		pbSpdu	: pointer to SPDU buffer to decode							*/
/*		spduLen		: length of SPDU buffer to decode							*/
/*									 										*/
/* OUTPUT PARAMETERS :							   							*/
/*		pbSpduTag		: type of SPDU												*/
/*		ssnb	: session number											*/
/*		pXpdu	: parameters dependent of the tag							*/
/*									    									*/
/* RETURN CODE								    							*/
/*		 -1 : error while decoding											*/
/*		 -2 : bad length_field value										*/
/*		> 0 : length of decoded SPDU 										*/
/*									  										*/
/* ======================================================================== */
int SESS_Decode(unsigned char *pbSpdu, unsigned long spduLen, 
				unsigned char *pbSpduTag, 
				unsigned short *ssnb, xpdu_t *pXpdu)
{            
	unsigned char*	ptr = NULL;			/* pointer to SPDU 				*/
	int				lengthField = 0;	/* length of SPDU data field 	*/

	SM_TRACE_ENTRY(TRMOD_SESS, "SESS_Decode() - entry,pbSpdu=0x%08X,"
				"spduLen=%d,pbSpduTag=0x%08X,ssnb=0x%08X,pXpdu=0x%08X\r\n",
				pbSpdu, pbSpduTag, ssnb, pXpdu);


	memset(pXpdu, 0x00, sizeof(xpdu_t));

	ptr = pbSpdu;
	*pbSpduTag = *ptr++;						/* SPDU's type 	*/
	lengthField = *ptr++;						/* length_field */		

	pXpdu->m_xpdu_tag = *pbSpduTag;
	
	SM_TRACE_BYTES(TRMOD_SESS, TRLEV_NOTIFY, pbSpdu, spduLen, 
				"SESS_Decode() - decode spdu[pbSpduTag=%s(0x%02X),lengthField=%d]:",
				XMSG_NAME(*pbSpduTag), *pbSpduTag, lengthField);

	switch(*pbSpduTag)
	{  
	case dutag_session_number :
		if (lengthField != 2)
		{
			SM_TRACE_ERROR(TRMOD_SESS, "SESS_Decode() - invalid lengthField=%d\r\n", lengthField);
			return 0; //-2;
		}
		SESS_DecodSsnb(ptr, ssnb);	/* ssnb */	
		ptr += 2;
		pXpdu->m_du.m_xpdu_session_number.m_pbData = ptr;	/* APDU			  */ 
		pXpdu->m_du.m_xpdu_session_number.m_len = spduLen - 4;
		pXpdu->m_du.m_xpdu_session_number.m_ssnb = *ssnb;
		lengthField = spduLen;
		break;

	case dutag_open_session_request :
		if (lengthField != RESOURCE_ID_SIZE)
		{
			SM_TRACE_ERROR(TRMOD_SESS, "SESS_Decode() - invalid lengthField=%d\r\n", lengthField);
			return 0; //-2;
		}
		pXpdu->m_du.m_xpdu_open_session_request.m_ressId = (((unsigned long)ptr[0])<<24)
													  | (((unsigned long)ptr[1])<<16)
													  | (((unsigned long)ptr[2])<<8)
													  | ((unsigned long)ptr[3]);
		if(spduLen != RESOURCE_ID_SIZE + 2)
		{
			SM_TRACE_ERROR(TRMOD_SESS, "SESS_Decode() - invalid lengthField=%d\r\n", lengthField);
			return 0; //-2;
		}
		else
			lengthField = spduLen;	
		break;			

	case dutag_create_session_response :
		if (lengthField != RESOURCE_ID_SIZE + 3)
		{
			SM_TRACE_ERROR(TRMOD_SESS, "SESS_Decode() - invalid lengthField=%d\r\n", lengthField);
			return 0; //-2;  
		}
		pXpdu->m_du.m_xpdu_create_session_response.m_status = *ptr++;					/* session_status 	   */
		pXpdu->m_du.m_xpdu_create_session_response.m_ressId = (((unsigned long)ptr[0])<<24)
													  | (((unsigned long)ptr[1])<<16)
													  | (((unsigned long)ptr[2])<<8)
													  | ((unsigned long)ptr[3]);	
		ptr += RESOURCE_ID_SIZE;
		SESS_DecodSsnb(ptr, ssnb);						/* ssnb	   */
		pXpdu->m_du.m_xpdu_create_session_response.m_ssnb = *ssnb;
		if(spduLen != RESOURCE_ID_SIZE + 5)
		{
			SM_TRACE_ERROR(TRMOD_SESS, "SESS_Decode() - invalid lengthField=%d\r\n", lengthField);
			return 0; //-2;
		}
		else
			lengthField = spduLen;	
		break;			

	case dutag_open_session_response :
		if (lengthField != RESOURCE_ID_SIZE + 3)
		{
			SM_TRACE_ERROR(TRMOD_SESS, "SESS_Decode() - invalid lengthField=%d\r\n", lengthField);
			return 0; //-2;  
		}
		pXpdu->m_du.m_xpdu_open_session_response.m_status = *ptr++;					/* session_status 	   */
		pXpdu->m_du.m_xpdu_open_session_response.m_ressId = (((unsigned long)ptr[0])<<24)
													  | (((unsigned long)ptr[1])<<16)
													  | (((unsigned long)ptr[2])<<8)
													  | ((unsigned long)ptr[3]);		
		ptr += RESOURCE_ID_SIZE;
		SESS_DecodSsnb(ptr, ssnb);						/* ssnb	   */
		pXpdu->m_du.m_xpdu_open_session_response.m_ssnb = *ssnb;
		if(spduLen != RESOURCE_ID_SIZE + 5)
		{
			SM_TRACE_ERROR(TRMOD_SESS, "SESS_Decode() - invalid lengthField=%d\r\n", lengthField);
			return 0; //-2;
		}
		else
			lengthField = spduLen;	
		break;	

	case dutag_close_session_request :
		if (lengthField != 2)
		{
			SM_TRACE_ERROR(TRMOD_SESS, "SESS_Decode() - invalid lengthField=%d\r\n", lengthField);
			return 0; //-2;  
		}
		SESS_DecodSsnb(ptr, ssnb);						/* ssnb	   */
		pXpdu->m_du.m_xpdu_close_session_request.m_ssnb = *ssnb;
		if(spduLen != 4)
		{
			SM_TRACE_ERROR(TRMOD_SESS, "SESS_Decode() - invalid spdu_tag=0x%02X\r\n", *pbSpduTag);
			return 0; //-2;
		}
		else
			lengthField = spduLen;	
		break;			

	case dutag_close_session_response :
		if (lengthField != 3)
		{
			SM_TRACE_ERROR(TRMOD_SESS, "SESS_Decode() - invalid lengthField=%d\r\n", lengthField);
			return 0; //-2;  
		}
		pXpdu->m_du.m_xpdu_close_session_response.m_status = *ptr++;					/* session_status 	   */
		SESS_DecodSsnb(ptr, ssnb);						/* ssnb	   */
		pXpdu->m_du.m_xpdu_close_session_response.m_ssnb = *ssnb;
		if(spduLen != 5)
		{
			SM_TRACE_ERROR(TRMOD_SESS, "SESS_Decode() - invalid lengthField=%d\r\n", lengthField);
			return 0; //-2;
		}
		else
			lengthField = spduLen;	
		break;			

	default : 
		SM_TRACE_ERROR(TRMOD_SESS, "SESS_Decode() - invalid lengthField=%d\r\n", lengthField);
		lengthField = 0; //-1;		/* Error : unknown spdu */
		break;						
	}

	SM_TRACE_RETURN(TRMOD_SESS, "SESS_Decode() - return,lengthField=%d\r\n",lengthField);
	return lengthField;
}


//add by pmy
BOOL SESS_XmsgDecode(xmsg_t* pXmsg, unsigned char *pbSpduTag, 
					unsigned short* pSsnb, xpdu_t *pXpdu)
{				
	unsigned char spduTag = 0;
	unsigned short ssnb = 0;
	unsigned long spduLen = 0;
	xpdu_t	xpdu;
	xpdu_t*	pInnerXpdu = NULL;

	if (pbSpduTag) *pbSpduTag = 0;
	if (pSsnb) *pSsnb = 0;
	if (pXpdu)
	{
		memset(pXpdu, 0x00, sizeof(xpdu_t));
		pInnerXpdu = pXpdu;
	}
	else
	{
		memset(&xpdu, 0x00, sizeof(xpdu));
		pInnerXpdu = &xpdu;
	}
	
	if (!pXmsg)
		return FALSE;
	
	if (pXmsg->m_type != SPDU)
		return FALSE;

	spduLen = SESS_Decode(pXmsg->m_bd.m_spdu.m_pbSpdu, 
						pXmsg->m_bd.m_spdu.m_len, &spduTag, &ssnb, pInnerXpdu);
	if (spduLen != pXmsg->m_bd.m_spdu.m_len)
	{
		SM_TRACE_ERROR(TRMOD_SESS, "SESS_SpduDecode() - Decode Error while decoding a SPDU "
					"tag=0x%x ssnb=%d spduLen=%d\r\n", spduTag, *pSsnb, spduLen);			
		return FALSE;
	}
	
	if (pbSpduTag) *pbSpduTag = spduTag;
	if (pSsnb) *pSsnb = ssnb;
	if (pXpdu) *pXpdu = *pInnerXpdu;

	return TRUE;
}


/* ======================================================================== */
/* NAME : SESS_CodSsnb						 								*/
/* 									    									*/
/* DESCRIPTION : code the session number field in SPDU						*/
/* 									    									*/
/* INPUT PARAMETERS :							    						*/
/*		ssnb : SSNB to code in SPDU											*/
/*									 										*/
/* OUTPUT PARAMETERS :							   							*/
/*		pbSpdu :	 coded SPDU													*/
/*									    									*/
/* RETURN CODE								    							*/
/* ======================================================================== */
void SESS_CodSsnb(unsigned short ssnb, unsigned char *pbSpdu)
{         
	*pbSpdu     = (unsigned char)(ssnb>> 8);
	*(pbSpdu+1) = (unsigned char)ssnb;
	
	return;
}



/* ======================================================================== */
/* NAME : SESS_DecodSsnb						 							*/
/* 									    									*/
/* DESCRIPTION : decode the session number field of SPDU					*/
/* 									    									*/
/* INPUT PARAMETERS :							    						*/
/*		pbSpdu :	 coded SPDU													*/
/*									 										*/
/* OUTPUT PARAMETERS :							   							*/
/*		ssnb : SSNB to decode in SPDU										*/
/*									    									*/
/* RETURN CODE								    							*/
/* ======================================================================== */
void SESS_DecodSsnb(unsigned char *pbSpdu, unsigned short *ssnb)
{         
	*ssnb = ((unsigned short)*pbSpdu * 256) + (unsigned short)*(pbSpdu+1);
			
	return;
}


